<?php

  /**
   * XMLController
   *
   * Класс контроллера для работы с XML-представлениями
   *
   * @property-read string         $pageTitle заголовок страницы
   * @property-read WebApplication $app       ссылка на экземпляр приложения
   * @property-read bool           $isDefault является умолчательным контроллером
   */

  abstract class XMLController extends CController
  {
    /**
     * рендеринг представления
     *
     * @param string $view          имя представления
     * @param array  $data          данные для рендеринга
     * @param bool   $return        вернуть или вывести
     * @param bool   $processOutput
     * @return string
     * @access public
     */
    public function renderPartial($view, $data = null, $return = false, $processOutput = false)
    {
      try
      {
        return parent::renderPartial($view, $data, $return, $processOutput);
      }
      catch (CException $e)
      {
        if ($return)
          return "";
      }
    }

    /**
     * рендеринг представления
     *
     * @param string $view   имя представления
     * @param array  $data   данные для рендеринга
     * @param bool   $return вернуть или вывести
     * @return string
     * @access public
     */
    public function render($view, $data = null, $return = false)
    {
      return $this->renderXML(parent::render($view, $data, true), $return, $this->getXSLUrl($view));
    }

    /**
     * вывод XML
     *
     * @param string $output       xml-текст для вывода
     * @param bool   $return       вернуть или вывести
     * @param string $xsl          таблица преобразований
     * @param bool   $makeEntities дополнить сущностями
     * @return mixed
     * @access public
     */
    public function renderXML($output, $return = false, $xsl = "", $makeEntities = true)
    {
      if ($makeEntities && count($htmlEntities = self::htmlEntities()))
      {
        $entities = "";
        foreach ($htmlEntities as $entity => $code)
          $entities .= "<!ENTITY $entity \"&#$code;\">\n";
        $output = "<!DOCTYPE xsl:stylesheet[\n$entities]>\n$output";
      }

      $xml = new DOMDocument("1.0", "utf-8");
      $xml->loadXML($output);
      if ($output = $xsl)
      {
        $xsl = new DOMDocument("1.0", "utf-8");
        $xsl->load($_SERVER["DOCUMENT_ROOT"].$output);
        $xsl->loadXML(preg_replace("/(<xsl:include\s+href=['\"])(.+?)(['\"]\s*\/>)/i", "$1$_SERVER[DOCUMENT_ROOT]$2$3", $xsl->saveXML()));
        $xslt = new XSLTProcessor();
        $xslt->importStylesheet($xsl);
        $html   = $xslt->transformToDoc($xml);
        $output = $html->saveHTML();
        $type   = "html";
      }
      else
      {
        $output = $xml->saveXML();
        $type   = "xml";
      }

      if ($return)
        return $output;
      else
        WebApplication::response($output, "text/$type");
    }

    /**
     * рендеринг представления
     *
     * @param array  $data   данные для рендеринга
     * @param string $view   представление
     * @param bool   $return вернуть или вывести
     * @return string
     * @access public
     */
    public function viewOutput(array $data = null, $view = "", $return = true)
    {
      return $this->renderPartial($view ? $view : $this->getAction()->getId(), $data, $return);
    }

    /**
     * вернуть URL XSLT-преобразований для указанного XML-представления
     *
     * @param string $view имя представления
     * @param string $id   идентификатор контроллера
     * @return string
     * @access public
     */
    public function getXSLUrl($view, $id = "")
    {
      $id or ($id = $this->getId());

      $xslPath = WebApplication::createPath(array($this->getApp()->getContentPath(), "xsl"));
      $xslUrl  = "{$this->app->contentUrl}/xsl";
      $view   .= ".xsl";
      $view    = file_exists(WebApplication::createPath(array($xslPath, $id, $view))) ? "$id/$view" : "main.xsl";

      return file_exists(WebApplication::createPath(array($xslPath, $view))) ? "$xslUrl/$view" : "";
    }

    /**
     * заголовок страницы для указанного метода
     *
     * @param string $action имя метода
     * @return string
     * @access public
     */
    public function getActionTitle($action)
    {
      $titles = &$this->app->titles;
      if (isset($titles[$id = $this->getId()][$action]))
        $titles = &$titles[$id];
      return array_key_exists($action, $titles) ? $titles[$action] : ucfirst($action ? $action : $id);
    }

    /**
     * заголовок страницы
     *
     * @return string
     * @access public
     */
    public function getPageTitle()
    {
      return "{$this->app->name} | ".$this->getActionTitle($this->getAction()->getId());
    }

    /**
     * ссылка на экземпляр приложения
     *
     * @return WebApplication
     * @access public
     */
    public function getApp()
    {
      return Yii::app();
    }

    /**
     * является умолчательным контроллером
     *
     * @return bool
     * @access public
     */
    public function getIsDefault()
    {
      return !strcasecmp($this->getId(), $this->getApp()->defaultController);
    }

    /**
     * проверить доступ
     *
     * @param string $access
     * @param bool   $raise возбудить исключение
     * @param array  $data  параметры
     * @return bool
     * @access protected
     */
    public function checkAccess($access, $raise = true, array $data = array())
    {
      $valid = $this->getApp()->getUser()->checkAccess($access, $data);
      $raise and WebApplication::validate($valid, 403);
      return $valid;
    }

    /**
     * выполнить предустановленное действие
     *
     * @param array $config параметры
     * @return void
     * @access protected
     */
    protected function runPreAction(array $config = array())
    {
      $action = $this->getAction()->getId();
      $this->createActionFromMap($this->actions(), $action, $action, $config)->run();
    }

    /**
     * вернуть присланный параметр
     *
     * @param string $name   имя параметра
     * @param mixed  $defVal умолчательное значение
     * @return mixed
     * @access protected
     */
    protected function getParam($name, $defVal = null)
    {
      return $this->getApp()->getRequest()->getPost($name, $defVal);
    }

    /**
     * "булевский" ответ на ajax-запрос
     *
     * @param mixed $expression возвращаемое выражение
     * @return void
     * @access protected
     */
    protected static function responseBool($expression)
    {
      WebApplication::response(CJSON::encode($expression), "application/json");
    }

    /**
     * сущности для XSLT-преобразований
     *
     * @return int[]
     * @access protected
     */
    protected static function htmlEntities()
    {
      return array(
        "apos"   => 146,
        "nbsp"   => 160,
        "copy"   => 169,
        "laquo"  => 171,
        "deg"    => 176,
        "plusmn" => 177,
        "sup2"   => 178,
        "sup3"   => 179,
        "ordm"   => 186,
        "raquo"  => 187,
        "times"  => 215,
        "ndash"  => 8211,
        "mdash"  => 8212,
        "ldquo"  => 8220,
        "bdquo"  => 8222,
        "bull"   => 8226,
        "trade"  => 8482,
        "minus"  => 8722
      );
    }
  }

?>